#include <cstdlib>
#include <iostream>
#include <fstream>
#include "hufftree.h"
#include "minqueue.h"
#include "header.h"

#define DISP_
using namespace std;

Hufftree::Hufftree (const map<char, int>& inmap) {
	if (inmap.empty()) {
		cout<<"there map is empty. Exit"<<endl;
		exit(1);
	}

	vector<tnode*> leaves;
	map<char, int>::const_iterator it = inmap.begin();
	while (it !=inmap.end()) {
		tnode* temp = new tnode(it->first, it->second);
		leaves.push_back(temp);
		++it;
	}
	
	MinQueue<tnode> mq;
	for (int i=0; i<leaves.size(); i++) {
		mq.enqueue(leaves[i], leaves[i]->freq);
	}
	
	while(!mq.isroot()) {
		tnode* temp1 = mq.dequeue();
		#ifndef DISP_
		if (temp1->lChild == 0)
			cout<<"\'"<<temp1->data<<"\', and ";
		else
			cout<<"InternalNode, and ";
		#endif
		tnode* temp2 = mq.dequeue();
		#ifndef DISP_
		if (temp2->lChild == 0)
			cout<<"\'"<<temp2->data<<"\' are dequeued, ";
		else
			cout<<"InternalNode are dequeued";
		#endif
		tnode* newnode = new tnode(temp1, temp2);
		mq.enqueue(newnode, newnode->freq);
		#ifndef DISP_
		cout<<"the new freq is: "<<newnode->freq<<endl;
		#endif
	}
	root = mq.dequeue(); /* mq is now empty */

	makecode(root,2);
}

void Hufftree::makecode(tnode* subroot, int lor) {
	static vector<bool> codeword;
	/* if this node is reached by going left, then push 0, else push 1 */
	if (lor == 0)
		codeword.push_back(false);
	else if (lor == 1)
		codeword.push_back(true);
	else
		codeword.clear(); /* start from root, clear the codeword */

	if (subroot->lChild == 0) {
		codebook[subroot->data] = codeword;
		#ifndef DISP_
		cout<<"Reached node: <\'"<<subroot->data<<"\', "<<subroot->freq<<">, ";
		for (int i=0; i<codeword.size(); i++)
			cout<<codeword[i];
		cout<<endl;
		#endif
	} else {
		makecode(subroot->rChild, 1);
		makecode(subroot->lChild, 0);
	}
	codeword.pop_back();
}

void Hufftree::checkcode () {
	map<char, vector<bool> >::iterator it = codebook.begin();
	while (it != codebook.end()){
		cout<<it->first<<": ";
		for(int i=0; i<it->second.size(); i++){
			cout<<it->second[i];
		}
		cout<<endl;
		++it;
	}
}

void Hufftree::encode(const char* filename) {
	if (filename == NULL) {
		exit (0);
	}

	string buffer;
	ifstream in(filename);
	if (!in) {
		cout<<"Cannot open "<<filename<<". Exit"<<endl;
		exit(1);
	}

	float ncnt=0;
	float ocnt=0;
	ofstream out("code.bin", ofstream::binary);
	map<char, vector<bool> >::iterator it = codebook.begin();
	while (it != codebook.end()) {
		out<<it->first<<" "<<it->second<<endl;
		++it;
	}
	out<<"*************************************"<<endl;
	while(getline(in, buffer)) {
		for(int i=0; i<buffer.size(); i++) {
			out<<codebook[buffer[i]];
			ocnt++;
			ncnt += codebook[buffer[i]].size();
		}
		out<<endl;
	}
	in.close();
	out.close();
	cout<<"###################################################"<<endl;
	cout<<"## the source file has been coded into code.bin!"<<endl;
	cout<<"## original file size: "<<ocnt*8/1000<<" Byte"<<endl;
	cout<<"## new file size: "<<ncnt/1000<<" Byte"<<endl;
	cout<<"## compresion ratio: "<<ocnt*8/ncnt<<endl;
	cout<<"###################################################"<<endl;
}

